﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

using RedirectManager.Interfaces;
using RedirectManager.Pipelines.HttpRequest;

using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Links;
using Sitecore.Pipelines.GetContentEditorWarnings;
using Sitecore.Sites;

namespace RedirectManager.Pipelines.GetContentEditorWarnings
{
    public class DisplayLinks
    {
        private enum DisplayModeEnum
        {
            Primary, Redirects, Aliases
        }

        private const string LimitResultsFormat = "<div style=\"Width:500px\" readonly=\"readonly\"><br />Limited to displaying the first {0} of {1} items</div>";
        private const string LinkFormat = "<div><a href=\"{0}\" target=\"_blank\"><input class=\"scEditorHeaderQuickInfoInput\" style=\"Width:500px\" readonly=\"readonly\" value=\"{0}\" /></a></div>"; // onclick=\"javascript:this.select();return false\"
        private const string RedirectFormat = @"<div><span style=""float:left; width:300px"">" +
            @"{5}<a href=""{0}"" target=""_blank"">{0}</a></span >&nbsp;" + //  onclick=""javascript:this.select();return false""
            @"<span style=""width:50px"">{1}</span>&nbsp;" + // delete
            @"<span style=""width:60px"">[ {2} ]</span>&nbsp;" + // enabled
            @"<span style=""width:60px"">[ {3} ]</span>&nbsp;" + // status code
            @"<span>{4}</span></div>"; // sites


        public void Process(GetContentEditorWarningsArgs args)
        {
            Assert.IsNotNull(args, "Redirect Manager: displaylinks GetContentEditorWarningsArgs");

            // Get site context.
            var siteContext = ResolveSiteContext(args.Item);
            if (siteContext == null)
            {
                Log.Error("Redirect Manager: displaylinks could not resolve site", this);
                return;
            }


            // Display primary link
            if (DisplayModeEnabled(DisplayModeEnum.Primary))
            {
                if (!args.Item.Paths.IsMediaItem && args.Item.Visualization.Layout != null)
                {
                    var naturalLinksSb = new StringBuilder();
                    //var options = GetUrlOptions(siteContext);
                    var options = LinkManager.GetDefaultUrlOptions();
                    options.Language = args.Item.Language; // AlwaysIncludeServerUrl=false is ignored when site passed in?
                    options.SiteResolving = true;
                    var primaryLinkString = LinkManager.GetItemUrl(args.Item, options);
                    //Uri primaryLinkUri;
                    //bool parsedAsUri = Uri.TryCreate(primaryLinkString, UriKind.Absolute, out primaryLinkUri);
                    naturalLinksSb.AppendFormat(LinkFormat, primaryLinkString);
                    if (naturalLinksSb.Length > 0)
                    {
                        args.Add("Primary Link", naturalLinksSb.ToString());
                    }
                }
            }

            // Display redirect paths
            if (DisplayModeEnabled(DisplayModeEnum.Redirects))
            {
                var redirectLinksSb = new StringBuilder();
                int index = 1;
                var redirectItems = GetRedirectItems(args.Item);
                if (redirectItems != null)
                {
                    foreach (IRedirect redirect in redirectItems)
                    {
                        // Do not display redirect if it relates to the current item path
                        bool pathMatchesContextItem = this.IsSamePath(args, siteContext, redirect.RequestPath);

                        // compare context url path with redirect path. Do not display if they match. We don't want to show a self-referencing redirect 
                        if (!pathMatchesContextItem)
                        {
                            // Iterate sites to check which are enabled
                            if (Config.SiteContextChecking)
                            {
                                var sitesMarkup = string.Empty;

                                //TODO: Allow site seletion.
                                // Define sites markup, if any
                                //if (redirect.Sites != null)
                                //{
                                //    foreach (var site in redirect.Sites.Select(Factory.GetSite))
                                //    {
                                //        sitesMarkup += string.Format("<a title='{0}' href='{0}' target=\"_blank\">{1}</a> | ", "http://" + (site.TargetHostName ?? site.HostName) + redirect.RequestPath, site.Name);
                                //    }
                                //    sitesMarkup = sitesMarkup.Trim().TrimEnd('|');
                                //    sitesMarkup = " sites : [ " + sitesMarkup + "]";
                                //}

                                // Define delete button
                                var deleteMarkup = string.Format("<a href=\"#\" onclick='javascript:return scForm.invoke(\"{0}\")' title=\"Delete redirect\">[ Delete ]</a>", string.Format("redirect:delete(targetId={0}, requestPath={1})", args.Item.ID, HttpUtility.UrlEncode(redirect.RequestPath)));
                                
                                // If language prefix are not disabled, indicate that language prefixes can apply to displayed redirect urls
                                string langPrefix = string.Empty;
                                if (LinkManager.LanguageEmbedding != LanguageEmbedding.Never)
                                {
                                    langPrefix = "<span style=\"color:#999999\">/en</span>";
                                }

                                // Format line
                                redirectLinksSb.AppendFormat(
                                    RedirectFormat,
                                    redirect.RequestPath,
                                    deleteMarkup,
                                    redirect.Enabled
                                        ? "Enabled"
                                        : "<span style=\"text-decoration:line-through; color:red\">Enabled</span>",
                                    redirect.ResponseStatusCode,
                                    sitesMarkup,
                                    langPrefix);
                            }
                            else
                            {
                                // If site context checking disabled, just show the path
                                redirectLinksSb.AppendFormat(LinkFormat, redirect.RequestPath);
                            }
                        }

                        // Limit to n number of items
                        if (index >= Config.DisplayMaxLinks)
                        {
                            redirectLinksSb.AppendFormat(LimitResultsFormat, Config.DisplayMaxLinks, redirectItems.Count());
                            break;
                        }

                        index++;
                    }
                }

                if (redirectLinksSb.Length > 0)
                {
                    args.Add("Urls that redirect here", redirectLinksSb.ToString());
                }
            }


            // Display alias links (Sitecore aliases)
            if (DisplayModeEnabled(DisplayModeEnum.Aliases))
            {
                var aliasLinksSb = new StringBuilder();
                var aliases = GetAliasItems(args.Item);
                var index = 1;

                if (aliases != null)
                {
                    foreach (Item alias in aliases)
                    {
                        var effectivePath = alias.Paths.Path.ToLowerInvariant().Replace("/sitecore/system/aliases".ToLowerInvariant(), string.Empty);
                        aliasLinksSb.AppendFormat(LinkFormat, effectivePath);

                        // Limit to n number of items
                        if (index >= Config.DisplayMaxLinks)
                        {
                            aliasLinksSb.AppendFormat(LimitResultsFormat, Config.DisplayMaxLinks, aliases.Count());
                            break;
                        }

                        index++;
                    }
                }

                if (aliasLinksSb.Length > 0)
                {
                    var areActive = Settings.AliasesActive ? string.Empty : " (aliases not active)";
                    args.Add("Aliases" + areActive, aliasLinksSb.ToString());
                }
            }
        }

        private bool IsSamePath(GetContentEditorWarningsArgs args, SiteContext siteContext, string requestPath)
        {
            var options = GetUrlOptions(siteContext);
            options.LanguageEmbedding = LanguageEmbedding.Never;
            options.AddAspxExtension = false;
            options.AlwaysIncludeServerUrl = false; // Ignored by LinkManager when site is set

            var itemPath = LinkManager.GetItemUrl(args.Item, options);
            
            bool samePath = itemPath.Equals(requestPath, StringComparison.OrdinalIgnoreCase)
                     || (itemPath + ".aspx").Equals(requestPath, StringComparison.OrdinalIgnoreCase);
            return samePath;
        }

        private static bool DisplayModeEnabled(DisplayModeEnum mode)
        {
            return Config.DisplayLinkTypes.Contains(mode.ToString(), StringComparer.OrdinalIgnoreCase);
        }

        private static UrlOptions GetUrlOptions(SiteContext siteContext)
        {
            var urlOptions = UrlOptions.DefaultOptions;
            urlOptions.AlwaysIncludeServerUrl = false; // changed to false to avoid having to deal with hostname resolution for link display.
            urlOptions.Site = siteContext;
            return urlOptions;
        }

        private static SiteContext ResolveSiteContext(Item item)
        {
            // Should perhaps be called TryResolveSitecoreContext...
            var siteContext = SiteManager.GetSites().Where(site => !Config.IgnoredSites.Contains(site.Name)).Select(
                    site => SiteContextFactory.GetSiteContext(site.Name)).Where(
                        sc => sc != null &&
                            item.Paths.Path.StartsWith(sc.RootPath, StringComparison.OrdinalIgnoreCase))
                            .FirstOrDefault();

            return siteContext;
        }

        private static IEnumerable<Item> GetAliasItems(Item item)
        {
            var linkDb = Factory.GetLinkDatabase();
            var aliasLinks =
                linkDb.GetReferrers(item).Where(link => link.SourceDatabaseName.Equals(item.Database.Name)).Where(
                    link => link.SourceFieldID.Equals(FieldIDs.AliasLinkedItem)).Where(
                        link => link.GetSourceItem().TemplateID.Equals(Sitecore.TemplateIDs.Alias));

            return aliasLinks.Select(link => link.GetSourceItem());
        }

        private static IEnumerable<IRedirect> GetRedirectItems(Item item)
        {
            ILookupProvider provider = Redirector.Provider;
            return provider.LookupItem(item.ID.ToString());
        }
    }
}
